﻿#region References
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using JigLibX.Physics;
using JigLibX.Collision;
using JigLibX.Geometry;
using Ness.Engine.GUI;
using Ness.Engine.Entities.Primitives;
using Xen;
using Xen.Camera;
using Xen.Graphics;
using Xen.Ex.Graphics;
using Xen.Ex.Graphics2D;
using Xen.Ex.Material;
using Xen.Graphics.State;
using Xen.Ex.Graphics.Content;
using Xen.Ex.Geometry;
using Xen.Ex;
#endregion

namespace Ness.Engine.Entities
{
    /// <summary>
    /// All dynamic and static props are handled through this class.
    /// NOT COMPLETED. LAGS WHEN USED.
    /// </summary>
    class Ragdoll : Entity, IUpdate
    {
        #region Public variables
        public Prop[] limbs;
        public HingeJoint[] joints;
        #endregion

        #region Private variables

        #endregion

        /// <summary>
        /// Iniialises the ragdoll, adds physics, constraints and collision data
        /// </summary>
        public Ragdoll(Engine game, string model, Vector3 position)
            : base(game, model, false)
        {
            //Set base variables
            this.game = game;
            this.boneAnimationTransforms = new ReadOnlyArrayCollection<Transform>();

            Xen.Ex.Graphics.Content.SkeletonData modelSkeleton = this.model.ModelData.Skeleton;
            int numLimbs = modelSkeleton.BoneCount;
            int numJoints = numLimbs * 2;
            limbs = new Prop[numLimbs];
            joints = new HingeJoint[numJoints];

            Vector3 becki = Vector3.One / 50;

           // MoveTo(position);

            this.WorldMatrix = Matrix.CreateScale(becki);
            WorldMatrix *= Matrix.CreateTranslation(position);

            List<string> boneList = new List<string>();
            boneList.Add("Bip01");
            boneList.Add("Bip01_Head");
            boneList.Add("Bip01_Neck");
            boneList.Add("Bip01_Spine");
            boneList.Add("Bip01_Spine1");
            boneList.Add("Bip01_Spine2");
            boneList.Add("Bip01_Spine3");

            //Add the bones
            Body prevBody = null;
                foreach (Xen.Ex.Graphics.Content.MeshData meshData in this.model.ModelData.Meshes)
                {
                    foreach (Xen.Ex.Graphics.Content.GeometryData geometry in meshData.Geometry)
                    {
                        //now loop through all bones used by this geometry

                        for (int geometryBone = 0; geometryBone < geometry.BoneIndices.Length; geometryBone++)
                        {
                            //Only parse certain bones
                            Debugger.WriteColour(modelSkeleton.BoneData[geometryBone].Name, ConsoleColor.Blue);
                           // if (boneList.Contains(modelSkeleton.BoneData[geometryBone].Name))
                           // {
                                //index of the bone (a piece of geometry may not use all the bones in the model)
                                int boneIndex = geometry.BoneIndices[geometryBone];
                                Xen.Ex.Graphics.Content.GeometryBounds bounds = geometry.BoneLocalBounds[geometryBone];

                                Transform boneTransform = modelSkeleton.BoneWorldTransforms[boneIndex];
                                Matrix matrix;

                                //if (this.boneAnimationTransforms.Length > 0) boneTransform *= this.boneAnimationTransforms[boneIndex];

                                boneTransform.GetMatrix(out matrix);

                                matrix *= this.WorldMatrix;

                                Vector3 sideLengths = ((bounds.Maximum - bounds.Minimum) * becki);
                                Vector3 tPos = Vector3.Transform(position - sideLengths, matrix);

                                limbs[boneIndex] = new Ness.Engine.Entities.Primitives.BoxObject(game, "models/box", sideLengths, Matrix.Identity, tPos);

                                // limbs[boneIndex].PhysicsBody.Position = tPos;
                                // limbs[boneIndex].collision = new CollisionSkin(limbs[boneIndex].PhysicsBody);

                                //   limbs[boneIndex].scale = sideLengths;
                                 limbs[boneIndex].PhysicsBody.DisableBody();
                                //limbs[boneIndex].PhysicsBody.EnableBody();
                                //limbs[boneIndex].collision.AddPrimitive(new Box(-0.5f * sideLengths, matrix, sideLengths), new MaterialProperties(0.8f, 0.8f, 0.7f));

                                // boneTransform.Scale = 10f;

                                game.AddToScene(limbs[boneIndex]);

                                //No-collide with previous bone
                                if (prevBody != null)
                                {
                                    DisableCollisions(limbs[boneIndex].PhysicsBody, prevBody);

                                }
                                prevBody = limbs[boneIndex].PhysicsBody;
                          //  }
                        }
                    }
                }

            //Enable joints and bodies
            foreach (HingeJoint joint in joints)
            {
                if (joint != null)
                    joint.EnableHinge();
            }

        }

        /// <summary>
        /// Puts every limb to "sleep"
        /// </summary>
        public void PutToSleep()
        {
            foreach (Prop limb in limbs)
            {
                if (limb != null)
                    limb.PhysicsBody.SetInactive();
            }
        }

        /// <summary>
        /// Disables collisions between limbs
        /// </summary>
        /// <param name="rb0"></param>
        /// <param name="rb1"></param>
        private void DisableCollisions(Body rb0, Body rb1)
        {
            if ((rb0.CollisionSkin == null) || (rb1.CollisionSkin == null))
                return;
            rb0.CollisionSkin.NonCollidables.Add(rb1.CollisionSkin);
            rb1.CollisionSkin.NonCollidables.Add(rb0.CollisionSkin);
        }

        /// <summary>
        /// Updates the ragdoll's world position and physics
        /// </summary>
        /// <param name="state">Passed automatically. Ignore.</param>
        /// <returns>UpdateFrequency.FullUpdate60hz</returns>
        UpdateFrequency IUpdate.Update(UpdateState state)
        {
           /* foreach (Xen.Ex.Graphics.Content.MeshData meshData in this.model.ModelData.Meshes)
            {
                foreach (Xen.Ex.Graphics.Content.GeometryData geometry in meshData.Geometry)
                {
                    for (int geometryBone = 0; geometryBone < geometry.BoneIndices.Length; geometryBone++)
                    {
                        int boneIndex = geometry.BoneIndices[geometryBone];
                        Xen.Ex.Graphics.Content.GeometryBounds bounds = geometry.BoneLocalBounds[geometryBone];
                        Transform boneTransform = this.model.ModelData.Skeleton.BoneWorldTransforms[boneIndex];

                        //Set bone orientation to physics object

                    }
                }
            }*/

            return UpdateFrequency.FullUpdate60hzAsync;
        }
    }
}
